package net.skyaims.lori.context;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.Vector;

/**
 * Created by wujianing on 14-5-18.
 */
public abstract class ClassLoaderContext {

    private static final Logger log = LoggerFactory.getLogger(ClassLoaderContext.class);

    private static final String CLASS_SUFFIX = ".class";

    private ByteClassLoader byteClassLoader = new ByteClassLoader();
    private ClassLoader currentThreadContextClassloader;
    private Field classLoaderClassesField;

    public ClassLoader getClassLoader() {
        return currentThreadContextClassloader;
    }

    public ClassLoaderContext() {
        try {
            currentThreadContextClassloader = Thread.currentThread().getContextClassLoader();
            classLoaderClassesField = ClassLoader.class.getDeclaredField("classes");
            classLoaderClassesField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("currentThread context classloader not support access filed 'classes'", e);
        }
    }

    /**
     * 如果存在则返回,没有则加载,加载不到抛异常
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    protected Class<?> loadClass(String className) throws ClassNotFoundException {

        log.info("class forName : {}", className);

        return Class.forName(getRegisterClassName(className));

    }

    /**
     * 在现有环境下查找,找到后返回,查找不到则抛异常
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    protected Class<?> findClass(String className) throws ClassNotFoundException {

        try {
            Vector<Class<?>> classes = (Vector<Class<?>>) classLoaderClassesField.get(currentThreadContextClassloader);
            for (Class<?> clz : classes) {
                if (className.equals(clz.getName())) {
                    return clz;
                }
            }
        } catch (IllegalAccessException e) {
            log.error(e.getMessage(), e);
        }

        throw new ClassNotFoundException(className);

    }

    protected Class<?> defineClassForBytes(String className, byte[] classBuffer) {

        log.info("define class : {} , buffer length : {}", className, classBuffer.length);

        return byteClassLoader.forBytes(getRegisterClassName(className), classBuffer);

    }

    private String getRegisterClassName(String className) {

        String realClassName;

        int classSuffixIndex = className.indexOf(CLASS_SUFFIX, className.length() - CLASS_SUFFIX.length());

        if (classSuffixIndex != -1) {
            realClassName = className.substring(0, classSuffixIndex);
        } else {
            realClassName = className;
        }

        return realClassName;

    }

    class ByteClassLoader extends ClassLoader {

        protected ByteClassLoader() {
            super(currentThreadContextClassloader);
        }

        public Class<?> forBytes(String className, byte[] classBuffer) {

            Class<?> clz = defineClass(className, classBuffer, 0, classBuffer.length);

            return clz;

        }

    }

}
